home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Freelog Special Edition 10
/
FreelogHS10.iso
/
Buzz
/
Buzz_Advanced_Pack.exe
/
{app}
/
Dev
/
elenzil_modulator.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
2001-08-27
|
17KB
|
816 lines
/*
The Elenzil Amplitude Modulator 1.0
20010303 Santa Cruz California
This machine was written primarily because Goenik's Amplitude Modulator
introduces small but noticable artifacts in the sound. For instance,
try tunning the M4 in the included demo song thru a single Goenik's
and compare to a single Elenzil. You may need to listen with headphones.
Since i used the AM far more than any other single effect,
i finally got tired of fixing the artifacts up in cooledit,
and went to write my own.
My guess at the cause of goenik's artifacts is that he calculated
the stereo spread only once per Work() routine, rather than for each Sample.
My version does it for each sample. This is probably overkill, but it works for me.
!!!
This machine is Crap for Efficiency.
I don't really care, as i usually have fast computers.
--> I am using actual Sin() calls, not lookup tables!
There is no justification for this except my own laziness.
Also, i built this off of an example from the standard buzz dist,
and havn't cleaned anything up.
If you fix it up, god bless you.
Okay,
that's about it.
Compiled w/ VC6.
Orion Elenzil
www.elenzil.com
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <math.h>
#include "../MachineInterface.h"
#include "../dsplib/dsplib.h"
#include <time.h>
double const SilentEnough = log(1.0 / 32768);
#define MAX_TAPS 1
CMachineParameter const paraSpeed =
{
pt_word, // type
"Speed",
"Oscillation Speed", // description
0, // MinValue
65534, // MaxValue
65535, // NoValue
MPF_STATE, // Flags
1000 // Default
};
#define UNIT_MHZ 0
#define UNIT_MS 1
#define UNIT_TICK 2
#define UNIT_256 3
CMachineParameter const paraUnit =
{
pt_byte, // type
"Speed Unit",
"Speed unit (0 = mHz, 1 = ms, 2 = tick, 3 = 256th of tick)",
0, // MinValue
3, // MaxValue
0xff, // NoValue
MPF_STATE, // Flags
UNIT_MHZ // Default
};
#define WAVE_SIN 0
#define WAVE_SQUARE 1
#define WAVE_TRIANGLE 2
#define WAVE_SAW 3
#define WAVE_INVSAW 4
#define WAVE_CRAZY 5
CMachineParameter const paraWave =
{
pt_byte, // type
"Wave Type",
"Wave Type (0 = sin, 1 = square, 2 = triangle 3 = saw, 4 = inv. saw)",
0, // MinValue
4, // MaxValue
0xff, // NoValue
MPF_STATE, // Flags
WAVE_SIN, // Default
};
CMachineParameter const paraWavePow =
{
pt_byte, // type
"Wave Power",
"Wave Power (1 = 1, 2 = ^2, 3 = ^3 etc)",
1, // MinValue
13, // MaxValue
0xff, // NoValue
MPF_STATE, // Flags
1, // Default
};
CMachineParameter const paraFloor =
{
pt_byte, // type
"Floor",
"Floor 00 - fe",
0, // MinValue
0xfe, // MaxValue
0xff, // NoValue
MPF_STATE, // Flags
0 // Default
};
CMachineParameter const paraSpread =
{
pt_word, // type
"Phase",
"Phase x0000 - x0800",
0x0000, // MinValue
0x0800, // MaxValue
0xffff, // NoValue
MPF_STATE, // Flags
0x0800, // Default
};
CMachineParameter const paraSlur =
{
pt_byte, // type
"Slur",
"Slur x00 - xfe",
0x00, // MinValue
0xfe, // MaxValue
0xff, // NoValue
MPF_STATE, // Flags
0xf2, // Default
};
CMachineParameter const paraGain =
{
pt_byte, // type
"Gain",
"Gain x00 - xfe",
0x00, // MinValue
0xf0, // MaxValue
0xff, // NoValue
MPF_STATE, // Flags
0x12, // Default
};
CMachineParameter const paraReset =
{
pt_switch, // type
"Reset",
"Reset 1 = Restart Oscillator",
0x01, // MinValue
0x01, // MaxValue
0xff, // NoValue
MPF_TICK_ON_EDIT, // Flags
0xff, // Default
};
CMachineParameter const *pParameters[] =
{
¶Speed,
¶Unit,
¶Wave,
¶WavePow,
¶Floor,
¶Spread,
¶Slur,
¶Gain,
¶Reset,
};
#pragma pack(1)
class tvals
{
public:
word speed;
byte unit;
byte wave;
byte wavepow;
byte floor;
word spread;
byte slur;
byte gain;
byte reset;
};
#pragma pack()
CMachineInfo const MacInfo =
{
MT_EFFECT, // type
MI_VERSION,
MIF_MONO_TO_STEREO, // flags
1, // min tracks
1, // max tracks
0, // numGlobalParameters
9, // numTrackParameters
pParameters,
0,
NULL,
#ifdef _DEBUG
"Elenzil Amplitude Modulator (Debug)", // name
#else
"Elenzil Amplitude Modulator",
#endif
"Modulator", // short name
"www.elenzil.com", // author
NULL
};
class CTrack
{
public:
double Speed;
int Unit;
int Wave;
int WavePow;
int Floor;
double Spread;
double Slur;
double Gain;
bool Reset;
double GetFrequency(CMasterInfo* pMasterInfo);
};
// Return frequency in seconds.
inline double CTrack::GetFrequency(CMasterInfo* pMasterInfo)
{
switch (Unit)
{
default:
case UNIT_MHZ:
return Speed * 0.001;
break;
case UNIT_MS:
if (Speed != 0.0)
return 1000.0 / Speed;
else
return 1000.0 / 0.5;
break;
case UNIT_TICK:
if (Speed != 0.0)
return pMasterInfo -> TicksPerSec / Speed;
else
return pMasterInfo -> TicksPerSec;
break;
case UNIT_256:
if (Speed != 0.0)
return 256.0 * pMasterInfo -> TicksPerSec / Speed;
else
return 256.0 * pMasterInfo -> TicksPerSec;
break;
}
}
class mi : public CMachineInterface
{
public:
mi();
virtual ~mi();
virtual void Init(CMachineDataInput * const pi);
virtual void Tick();
// virtual bool Work(float *psamples, int numsamples, int const mode);
virtual bool WorkMonoToStereo(float *pin, float *pout, int numsamples, int const mode);
virtual void SetNumTracks(int const n);
virtual void AttributesChanged();
virtual char const *DescribeValue(int const param, int const value);
private:
void InitTrack(int const i);
void ResetTrack(int const i);
void TickTrack(CTrack *pt, tvals *ptval);
void WorkTrack(CTrack *pt, float *pin, float *pout, int numsamples, int const mode);
int MSToSamples(double const ms);
private:
int IdleCount; // in samples
bool IdleMode;
int pos;
int pos2;
long m_Tick;
double m_Second;
double m_PrevF1;
double m_PrevF2;
private:
int numTracks;
CTrack Tracks[MAX_TAPS];
tvals tval[MAX_TAPS];
};
DLL_EXPORTS
mi::mi()
{
GlobalVals = NULL;
TrackVals = tval;
AttrVals = NULL;
}
mi::~mi()
{
}
char* CommaPrint(char* txt, int const value)
{
char tmp[32];
int i, inum;
int j, k;
sprintf(tmp, "%d", value);
inum = strlen(tmp);
j = inum % 3;
k = 0;
for (i = 0; i < inum; i++)
{
txt[k++] = tmp[i];
j--;
if (j == 0 && i < inum - 1)
{
txt[k++] = ',';
j = 3;
}
}
txt[k] = 0;
return txt;
}
char const *mi::DescribeValue(int const param, int const value)
{
static char txt[16];
char tmp[9];
int val;
val = value;
switch(param)
{
case 0: // freq
switch(Tracks[0].Unit)
{
default:
case UNIT_MS:
case UNIT_MHZ:
CommaPrint(txt, val);
sprintf(tmp, " (%.4x)", val);
strcat(txt, tmp);
break;
}
break;
case 1: // unit
switch(val)
{
case 0: return "mHz (00)";
case 1: return "ms (01)";
case 2: return "tick (02)";
case 3: return "tick/256 (03)";
}
break;
case 2: // wave
char* s;
switch(val)
{
case WAVE_SIN : s = "sin" ; break;
case WAVE_SQUARE : s = "square" ; break;
case WAVE_TRIANGLE : s = "triangle" ; break;
case WAVE_SAW : s = "saw" ; break;
case WAVE_INVSAW : s = "inv. saw" ; break;
case WAVE_CRAZY : s = "crazy" ; break;
}
sprintf(txt, "%s (%.2x)", s, val);
break;
case 3: // wave power
sprintf(txt, "^%d", val);
break;
case 4: // floor
sprintf(txt, "%d%c (%.2x)", val * 100 / paraFloor.MaxValue, '%', val);
break;
case 5: // spread
sprintf(txt, "%.3f (%.4x)", (double)val / (double)paraSpread.MaxValue * 2.0 - 1.0, val);
break;
case 6: // slur
sprintf(txt, "%.3f (%.2x)", (double)val / (double)paraSlur.MaxValue, val);
break;
case 7: // gain
sprintf(txt, "%.2f (%.2x)", (double)val / (double)paraGain.DefValue, val);
break;
case 8: // reset
sprintf(txt, "1 = reset oscillation");
break;
default:
return NULL;
}
return txt;
}
void mi::Init(CMachineDataInput * const pi)
{
numTracks = 1;
for (int c = 0; c < MAX_TAPS; c++)
{
Tracks[c].Speed = 1000.0;
Tracks[c].Unit = UNIT_MHZ;
Tracks[c].Wave = WAVE_SIN;
Tracks[c].WavePow = 1;
Tracks[c].Floor = 0;
Tracks[c].Spread = 1.0;
Tracks[c].Slur = 0.94;
Tracks[c].Gain = 1.0;
Tracks[c].Reset = false;
}
IdleMode = true;
IdleCount = 0;
pos = 0;
pos2 = 0;
m_Tick = 0;
m_Second = 0.0;
m_PrevF1 = 1.0;
m_PrevF2 = 1.0;
}
void mi::AttributesChanged()
{
for (int c = 0; c < numTracks; c++)
InitTrack(c);
}
void mi::SetNumTracks(int const n)
{
if (numTracks < n)
{
for (int c = numTracks; c < n; c++)
InitTrack(c);
}
else if (n < numTracks)
{
for (int c = n; c < numTracks; c++)
ResetTrack(c);
}
numTracks = n;
}
int mi::MSToSamples(double const ms)
{
return (int)(pMasterInfo->SamplesPerSec * ms * (1.0 / 1000.0));
}
void mi::InitTrack(int const i)
{
}
void mi::ResetTrack(int const i)
{
}
void mi::TickTrack(CTrack *pt, tvals *ptval)
{
if (ptval->speed != paraSpeed .NoValue)
pt->Speed = (double)ptval -> speed;
if (ptval->unit != paraUnit .NoValue)
pt->Unit = ptval -> unit;
if (ptval->wave != paraWave .NoValue)
pt->Wave = ptval -> wave;
if (ptval->wavepow != paraWavePow .NoValue)
pt->WavePow = ptval -> wavepow;
if (ptval->floor != paraFloor .NoValue)
pt->Floor = ptval -> floor;
if (ptval->spread != paraSpread .NoValue)
pt->Spread = (double)ptval -> spread / (double)paraSpread.MaxValue * 2.0 - 1.0;
if (ptval->slur != paraSlur .NoValue)
pt->Slur = (double)ptval -> slur / (double)paraSlur.MaxValue;
if (ptval->gain != paraGain .NoValue)
pt->Gain = (double)ptval -> gain / (double)paraGain.DefValue;
if (ptval->reset != paraReset .NoValue)
pt->Reset = (bool) ptval -> reset;
}
void mi::Tick()
{
m_Tick++;
for (int c = 0; c < numTracks; c++)
TickTrack(Tracks + c, tval+c);
}
bool mi::WorkMonoToStereo(float *pin, float *pout, int numsamples, int const mode)
{
if (mode != WM_READWRITE)
return false;
int i, inum;
int j;
int p, pnum;
inum = numsamples;
j = 0;
int op;
op = pos;
pos += pMasterInfo -> PosInTick - pos2;
pos2 = pMasterInfo -> PosInTick;
double f1;
double f2;
double SecondsPerSample;
double floor;
double one_minus_floor;
double Spread;
double Slur, OneMinusSlur;
double Gain;
floor = (double)(Tracks[0].Floor) / (double)paraFloor.MaxValue;
one_minus_floor = 1.0 - floor;
SecondsPerSample = 1.0 / (double)pMasterInfo -> SamplesPerSec;
SecondsPerSample *= Tracks[0].GetFrequency(pMasterInfo);
Spread = Tracks[0].Spread;
pnum = Tracks[0].WavePow;
Slur = Tracks[0].Slur;
OneMinusSlur = 1.0 - Slur;
Gain = Tracks[0].Gain;
if (Tracks[0].Reset)
{
m_Second = 0;
Tracks[0].Reset = false;
}
switch (Tracks[0].Wave)
{
default:
case WAVE_SIN:
Spread *= PI * 0.5;
for (i = 0; i < inum; i++)
{
f1 = sin(m_Second * PI * 2.0 - Spread) * 0.5 + 0.5;
f2 = sin(m_Second * PI * 2.0 + Spread) * 0.5 + 0.5;
m_Second += SecondsPerSample;
// This prevents the math from overflowing.
if (m_Second > 1.0)
m_Second -= 1.0;
for (p = 1; p < pnum; p++)
{
f1 *= f1;
f2 *= f2;
}
f1 = f1 * one_minus_floor + floor;
f2 = f2 * one_minus_floor + floor;
f1 *= Gain;
f2 *= Gain;
f1 = OneMinusSlur * f1 + Slur * m_PrevF1;
f2 = OneMinusSlur * f2 + Slur * m_PrevF2;
m_PrevF1 = f1;
m_PrevF2 = f2;
pout[j] = pin[i] * (float)f1;
j++;
pout[j] = pin[i] * (float)f2;
j++;
}
break;
case WAVE_SQUARE:
Spread *= 0.25;
for (i = 0; i < inum; i++)
{
f1 = m_Second - Spread + 0.25;
if (f1 < 0.0)
f1 += 1.0;
else if (f1 > 1.0)
f1 -= 1.0;
f2 = m_Second + Spread + 0.25;
if (f2 < 0.0)
f2 += 1.0;
else if (f2 > 1.0)
f2 -= 1.0;
if (f1 < 0.5)
f1 = 0.0;
else
f1 = 1.0;
if (f2 < 0.5)
f2 = 0.0;
else
f2 = 1.0;
m_Second += SecondsPerSample;
// This prevents the math from overflowing.
if (m_Second > 1.0)
m_Second -= 1.0;
f1 = f1 * one_minus_floor + floor;
f2 = f2 * one_minus_floor + floor;
f1 *= Gain;
f2 *= Gain;
f1 = OneMinusSlur * f1 + Slur * m_PrevF1;
f2 = OneMinusSlur * f2 + Slur * m_PrevF2;
m_PrevF1 = f1;
m_PrevF2 = f2;
pout[j] = pin[i] * (float)f1;
j++;
pout[j] = pin[i] * (float)f2;
j++;
}
break;
case WAVE_TRIANGLE:
Spread *= 0.25;
for (i = 0; i < inum; i++)
{
f1 = m_Second - Spread + 0.25;
if (f1 < 0.0)
f1 += 1.0;
else if (f1 > 1.0)
f1 -= 1.0;
f2 = m_Second + Spread + 0.25;
if (f2 < 0.0)
f2 += 1.0;
else if (f2 > 1.0)
f2 -= 1.0;
f1 *= 2.0;
f2 *= 2.0;
if (f1 > 1.0)
f1 = 2.0 - f1;
if (f2 > 1.0)
f2 = 2.0 - f2;
m_Second += SecondsPerSample;
// This prevents the math from overflowing.
if (m_Second > 1.0)
m_Second -= 1.0;
for (p = 1; p < pnum; p++)
{
f1 *= f1;
f2 *= f2;
}
f1 = f1 * one_minus_floor + floor;
f2 = f2 * one_minus_floor + floor;
f1 *= Gain;
f2 *= Gain;
f1 = OneMinusSlur * f1 + Slur * m_PrevF1;
f2 = OneMinusSlur * f2 + Slur * m_PrevF2;
m_PrevF1 = f1;
m_PrevF2 = f2;
pout[j] = pin[i] * (float)f1;
j++;
pout[j] = pin[i] * (float)f2;
j++;
}
break;
case WAVE_SAW:
Spread *= 0.25;
for (i = 0; i < inum; i++)
{
f1 = m_Second - Spread + 0.25;
if (f1 < 0.0)
f1 += 1.0;
else if (f1 > 1.0)
f1 -= 1.0;
f2 = m_Second + Spread + 0.25;
if (f2 < 0.0)
f2 += 1.0;
else if (f2 > 1.0)
f2 -= 1.0;
m_Second += SecondsPerSample;
// This prevents the math from overflowing.
if (m_Second > 1.0)
m_Second -= 1.0;
for (p = 1; p < pnum; p++)
{
f1 *= f1;
f2 *= f2;
}
f1 = f1 * one_minus_floor + floor;
f2 = f2 * one_minus_floor + floor;
f1 *= Gain;
f2 *= Gain;
f1 = OneMinusSlur * f1 + Slur * m_PrevF1;
f2 = OneMinusSlur * f2 + Slur * m_PrevF2;
m_PrevF1 = f1;
m_PrevF2 = f2;
pout[j] = pin[i] * (float)f1;
j++;
pout[j] = pin[i] * (float)f2;
j++;
}
break;
case WAVE_INVSAW:
Spread *= 0.25;
for (i = 0; i < inum; i++)
{
f1 = m_Second - Spread + 0.25;
if (f1 < 0.0)
f1 += 1.0;
else if (f1 > 1.0)
f1 -= 1.0;
f2 = m_Second + Spread + 0.25;
if (f2 < 0.0)
f2 += 1.0;
else if (f2 > 1.0)
f2 -= 1.0;
f1 = 1.0 - f1;
f2 = 1.0 - f2;
m_Second += SecondsPerSample;
// This prevents the math from overflowing.
if (m_Second > 1.0)
m_Second -= 1.0;
for (p = 1; p < pnum; p++)
{
f1 *= f1;
f2 *= f2;
}
f1 = f1 * one_minus_floor + floor;
f2 = f2 * one_minus_floor + floor;
f1 *= Gain;
f2 *= Gain;
f1 = OneMinusSlur * f1 + Slur * m_PrevF1;
f2 = OneMinusSlur * f2 + Slur * m_PrevF2;
m_PrevF1 = f1;
m_PrevF2 = f2;
pout[j] = pin[i] * (float)f1;
j++;
pout[j] = pin[i] * (float)f2;
j++;
}
break;
}
return true;
}